<?xml version="1.0" encoding="UTF-8"?><d:tdl xmlns="http://www.w3.org/1999/xhtml" xmlns:b="http://www.backbase.com/2006/btl" xmlns:bb="http://www.backbase.com/2006/client"  xmlns:d="http://www.backbase.com/2006/tdl" >

	<d:namespace name="http://www.backbase.com/2006/btl">

		<d:uses element="dataObserver" src="../dataBinding/dataBinding.xml"/>
		<d:uses element="dropDown" src="../dropDown/dropDown.xml"/>
		<d:uses element="formList formListOption" src="../formList/formList.xml"/>
		<d:uses element="positionElement dimensionElement" src="../visualElement/visualElement.xml"/>
		<d:uses element="focusableElement" src="../focus/focus.xml"/>

		<d:element name="suggestBoxBase" extends="b:dropDown b:formList b:dataObserver b:positionElement b:dimensionElement b:focusableElement" abstract="true">
			

			<d:resource type="text/javascript"><![CDATA[
				btl.suggestBoxBase = {};

				btl.suggestBoxBase.delaySuggest = function btl_suggestBoxBase_delaySuggest(){
					var iDelay = parseInt(btl.suggestBoxBase.current.getAttribute('suggestDelay'));
					var iKeyCount = btl.suggestBoxBase.keyUp;
					setTimeout(function(){ btl.suggestBoxBase.suggest(iKeyCount); }, iDelay)
				};

				btl.suggestBoxBase.suggest = function btl_suggestBoxBase_suggest(iKeyCount){
					if(iKeyCount == btl.suggestBoxBase.keyUp){
						btl.suggestBoxBase.keyUp = 0;
						if (btl.suggestBoxBase.current) 
							if(btl.suggestBoxBase.current._)
								btl.suggestBoxBase.current.suggest();
					}
				}

				btl.suggestBoxBase.current = null;

				btl.suggestBoxBase.keyUp = 0;
			]]></d:resource>

			<d:attribute name="suggestDelay" default="200">
				
			</d:attribute>

			<d:attribute name="suggestStartLength" default="1">
				
			</d:attribute>

			<d:attribute name="suggestMaximum">
				
			</d:attribute>

			<d:attribute name="select" default="*[1]">
				
			</d:attribute>

			<d:property name="label" onget="return this.getProperty('viewLabel')" onset="this.setProperty('viewLabel', value)">
				
			</d:property>

			<d:property name="value">
				<d:setter type="text/javascript"><![CDATA[
					var aOptions = this.getProperty('options');
					var iIndex = -1;
					var sLabel = value;

					for(var i = 0, iMax = aOptions.length; iMax > i; i++){
						if(aOptions[i].getProperty('value') == value){
							sLabel = aOptions[i].getProperty('text');
							iIndex = i;
							break;
						}
					}

					this.setProperty('label', sLabel);
					this.setProperty('viewValue', value);
				]]></d:setter>
				<d:getter type="text/javascript"><![CDATA[
					var iSelectedIndex = this.getProperty('selectedIndex');

					// by default, the value is what has been typed in
					var sValue = this.getProperty('label');

					if(iSelectedIndex > -1){
						var aOptions = this.getProperty('options');
						if(iSelectedIndex < aOptions.length){
							sValue = aOptions[iSelectedIndex].getProperty('value');
						}
					}
					return sValue;
				]]></d:getter>
			</d:property>

			<d:property name="highlightIndex">
				
				<d:setter type="text/javascript"><![CDATA[
					var iCurrentIndex = this.getProperty('highlightIndex');
					var aOptions = this.getProperty('options');
					var iOptionsLength = aOptions.length;

					if(iCurrentIndex != value){
						if(value > -1 && iOptionsLength > value){
							// highlight the option in the view
							aOptions[value].setProperty('highlight', true);
						}

						if(iCurrentIndex > -1 && iOptionsLength > iCurrentIndex){
							//remove highlight from the view
							aOptions[iCurrentIndex].setProperty('highlight', false);
						}

						if(value >= -1 && iOptionsLength > value){
							// store the value
							this._['_highlightIndex'] = value;
						}
					}
				]]></d:setter>
				<d:getter type="text/javascript"><![CDATA[
					if(this._['_highlightIndex'] === undefined)
						this._['_highlightIndex'] = -1;
					return this._['_highlightIndex'];
				]]></d:getter>
			</d:property>

			<d:property name="selectedIndex">
				<d:setter type="text/javascript"><![CDATA[
					// non option value
					if(value === -1){
						var iCurrentSelectedIndex = this.getProperty('selectedIndex');
						//deselect selected option
						if(iCurrentSelectedIndex >= 0){
							var aOptions = this.getProperty('options');
							aOptions[iCurrentSelectedIndex].setProperty('selected', false);
						}

						// then the new value is the value of the display
						var sNewValue = this.getProperty('label');

						/*
							Since we can not be sure that the value of the display is
							not actually a selected index, we will correct setting -1 to the proper
							index.
						*/
						sNewValue = sNewValue.toLowerCase();

						var aOptions = this.getProperty('options');
						var iIndex = -1;

						for(var i = 0, iMax = aOptions.length; iMax > i; i++){
							var sOptionLabel = aOptions[i].getProperty('text').toLowerCase();
							if(sNewValue === sOptionLabel) {
								iIndex = i;
								break;
							}
						}
						if(iIndex >= 0){
							this.setProperty('selectedIndex', iIndex);
							this.setProperty('highlightIndex', iIndex);
						} else {
							this.setProperty('highlightIndex', -1);
						}
					} else {
						var aOptions = this.getProperty('options');
						if (aOptions.length > value){
							var sNewLabel = aOptions[value].getProperty('text');
							var sNewValue = aOptions[value].getProperty('value');
							var iCurrentSelectedIndex = this.getProperty('selectedIndex');

							this.setProperty('label', sNewLabel);

							this.setProperty('value', sNewValue);

							//deselect selected option
							if(iCurrentSelectedIndex >= 0){
								aOptions[iCurrentSelectedIndex].setProperty('selected', false);
							}

							aOptions[value].setProperty('selected', true);
							this.setProperty('highlightIndex', value);
						}
					}
				]]></d:setter>
				<!-- REMOVE THE GETTER WHEN BUG 6509 IS FIXED -->
				<d:getter type="text/javascript"><![CDATA[
					// by default, the index is -1, unless an option is selected;
					var iSelectedIndex = -1;
					var aOptions = this.getProperty('options');
					for(var i = 0, iMax = aOptions.length; iMax > i; i++){
						if(aOptions[i].getProperty('selected')){
							iSelectedIndex = i;
							break
						}
					}
					return iSelectedIndex;
				]]></d:getter>
			</d:property>

			<d:method name="suggest">
				
				<d:body type="text/javascript"><![CDATA[
					this.close();

					// reset former values
					var aChildNodes = this.getProperty('childNodes');
					for(var j = 0, jMax = aChildNodes.length; jMax > j ; j++)
						if( aChildNodes[j] && bb.instanceOf(aChildNodes[j], btl.namespaceURI, 'suggestBoxOptionBase'))
							bb.destruct(aChildNodes[j]);

					this.setProperty('selectedIndex', -1);

					// get the new suggestions
					if(this.getProperty("value").length >= this.getAttribute("suggestStartLength")) {
						this.getSuggestions();
					}
				]]></d:body>
			</d:method>

			<d:method name="getSuggestions">
				
				<d:body type="text/javascript"><![CDATA[
					/*
						Refresh will trigger the dataUpdate method to be executed. See dataObserver
						for more details.
					*/
					this.refresh();
				]]></d:body>
			</d:method>

			<d:method name="dataUpdate">
				<d:argument name="action"/>
				<d:argument name="records"/>
				<d:argument name="actionObj"/>
				<d:body type="text/javascript"><![CDATA[
					this.callSuper('dataUpdate', [action, records, actionObj]);
					if (!(action == 'read' || action == 'sort'))
						return;

					var sLabel = this.getProperty('label');
					if (sLabel.length > 0) {
						sLowercaseLabel = sLabel.toLowerCase();

						var oSource = this.getProperty('dataSource');
						var sQuery = this.getAttribute('select');
						var aResults = [];
						var iSuggestMaximum = parseInt(this.getAttribute('suggestMaximum') || records.length);

						for (var i = 0, iMax = records.length; i < iMax && iSuggestMaximum > aResults.length; i++) {
							var sValue = btl.dataSource.getValue(oSource, records[i], sQuery);
							if (sValue.toLowerCase().indexOf(sLowercaseLabel) == 0)
								aResults[aResults.length] = sValue;
						}

						for (var j = 0, jMax = aResults.length; jMax > j; j++) {

							var oOption = bb.document.createElementNS(btl.namespaceURI, 'suggestBoxOption');
							var oOptionCol = bb.document.createElementNS(btl.namespaceURI, 'suggestBoxOptionCol');
							var oText = bb.document.createTextNode(aResults[j]);

							oOptionCol.appendChild(oText);
							oOption.appendChild(oOptionCol);
							this.appendChild(oOption);

							if (aResults[j] == sLabel)
								this.setProperty('selectedIndex', j);
						}
						if (this.bKeyPressed) {
							this.bKeyPressed = false;
							this.open();
						}
					}
				]]></d:body>
			</d:method>

			<d:method name="getValueFromHighlight">
				
				<d:body type="text/javascript"><![CDATA[
					var iHighlightIndex = this.getProperty('highlightIndex');

					// by default, the value is what has been typed in
					var sValue = this.getProperty('label');

					if(iHighlightIndex > -1){
						var aOptions = this.getProperty('options');
						if(iHighlightIndex < aOptions.length){
							sValue = aOptions[iHighlightIndex].getProperty('value');
						}
					}
					return sValue;
				]]></d:body>
			</d:method>

			<d:handler event="mousedown" type="text/javascript"><![CDATA[
				if(!bb.browser.ie && this.__mouseDown)
					event.preventDefault();
			]]></d:handler>

			<d:handler event="mouseup" type="text/javascript"><![CDATA[
				if (event.button == 0 && this.__mouseDown){
					this.setProperty('value', this.getValueFromHighlight());
					var iHighlightIndex = this.getProperty('highlightIndex');
					this.setProperty('selectedIndex', iHighlightIndex);
					this.close();
					bb.command.fireEvent(this, 'select', false, false);
				}

				this.__mouseDown = false;
			]]></d:handler>

			<d:handler event="mouseleave" type="text/javascript"><![CDATA[
				this.__mouseDown = false;
			]]></d:handler>

			<d:handler event="keydown" type="text/javascript"><![CDATA[
				if(this.getProperty('open')){
					switch( event.keyIdentifier ){
						case 'Down':
							// prevent scrolling
							event.preventDefault();

							var iHighlightIndex = this.getProperty('highlightIndex');
							var aOptions = this.getProperty('options');

							while(aOptions.length > iHighlightIndex){
								iHighlightIndex++;

								if (iHighlightIndex === aOptions.length){
									this.setProperty('highlightIndex', -1);
								} else {
									this.setProperty('highlightIndex', iHighlightIndex);
								}
								return;
							}

							break;

						case 'Up':
							// prevent scrolling
							event.preventDefault();

							var iHighlightIndex = this.getProperty('highlightIndex');
							var aOptions = this.getProperty('options');

							// if none highlighted, start from last
							if(iHighlightIndex === -1)
								iHighlightIndex = aOptions.length;

							while(iHighlightIndex > -1){
								iHighlightIndex--;
								this.setProperty('highlightIndex', iHighlightIndex);
								return;
							}
							break;

						case 'U+0020': // Space
							//Should only make a selection when it is open and when it's readonly (if not readonly, spaces are used for the value)
							var iHighlightIndex = this.getProperty('highlightIndex');
							if(this.getProperty('open') && iHighlightIndex != -1){
								// set the value
								this.setProperty('value', this.getValueFromHighlight());
								this.setProperty('selectedIndex', iHighlightIndex);
								bb.command.fireEvent(this, 'select', false, false);
								this.close();

								// dispatch change event
								var sValue = this.getProperty('value');
								if ( this.__oldValue != sValue ) {
									this.__oldValue = sValue;
									bb.command.fireEvent(this, 'change', true, false);
								}

								this.__spaceDown;
								event.preventDefault();
							}
							break;
						case 'Enter':
							var iHighlightIndex = this.getProperty('highlightIndex');
							if(this.getProperty('open') && iHighlightIndex != -1){
								// set the value
								this.setProperty('value', this.getValueFromHighlight());
								this.setProperty('selectedIndex', iHighlightIndex);
								bb.command.fireEvent(this, 'select', false, false);

								// dispatch change event
								var sValue = this.getProperty('value');
								if ( this.__oldValue != sValue ) {
									this.__oldValue = sValue;
									bb.command.fireEvent(this, 'change', true, false);
								}

								//prevent form submit
								event.preventDefault();
							}
							this.close();

							break;

						case 'U+001B': // Escape
							this.close();
							break;

						default:
							break;
					}
				} else {
					switch( event.keyIdentifier ){
						case 'Down':
						case 'Up':
							// prevent scrolling
							event.preventDefault();
							var aOptions = this.getProperty('options');
							if(aOptions.length)
								this.open();

							break;

						default:
							break;
					}
				}
			]]></d:handler>

			<d:handler event="keyup" type="text/javascript"><![CDATA[
				// setting this.bKeyPressed to true to allow the suggestBox to open when dataUpdate() is called.
				this.bKeyPressed = true;

				switch( event.keyIdentifier ){
					case 'U+0008': // backspace
					case 'U+002E': // delete
						btl.suggestBoxBase.current = this;
						btl.suggestBoxBase.keyUp++;
						btl.suggestBoxBase.delaySuggest();
						break;
				}
			]]></d:handler>

			<d:handler event="textInput" type="text/javascript"><![CDATA[
				btl.suggestBoxBase.current = this;
				btl.suggestBoxBase.keyUp++;
				btl.suggestBoxBase.delaySuggest();
			]]></d:handler>

			<d:handler event="focus" type="text/javascript"><![CDATA[
				//store current value to compare on blur
				this.__oldValue = this.getProperty('value');
			]]></d:handler>

			<d:handler event="blur" type="text/javascript"><![CDATA[
				if(!this.__mouseDown)
					this.close();

				var iSelectedIndex = this.getProperty('selectedIndex');

				// if the selectedIndex is -1, not an option from the list, the value that has been typed is the value
				if(iSelectedIndex === -1){
					// then the new value is the value of the display
					var sNewValue = this.getProperty('label');
					this.setProperty('value', sNewValue);
				}

				// dispatch change event when value is not the same as when the element was focussed
				if ( this.__oldValue != this.getProperty('value') )
					bb.command.fireEvent(this, 'change', true, false);
			]]></d:handler>
		</d:element>

		<d:element name="suggestBoxOptionBase" extends="b:formListOption" abstract="true">
			
			<d:property name="text">
				<d:getter type="text/javascript"><![CDATA[
					var sValue = '';
					var aChildren = this.getProperty('childNodes');
					var iMax = aChildren.length;
					if(iMax){
						for(var i = 0; iMax > i; i++){
							if(aChildren[i] && bb.instanceOf(aChildren[i], btl.namespaceURI, 'suggestBoxOptionColBase')){
								sValue = aChildren[i].getProperty('textContent');
								break;
							}
						}
					}
					return sValue;
				]]></d:getter>
			</d:property>
		</d:element>

		<d:element name="suggestBoxOptionColBase" extends="b:element" abstract="true">
			
		</d:element>

		<d:interface name="iSuggestBox">
			

			<d:property name="viewForm">
				
			</d:property>

			<d:property name="viewLabel">
				
			</d:property>

			<d:property name="viewValue">
				
			</d:property>
		</d:interface>
	</d:namespace>
</d:tdl>